#include <llvm/Pass.h>
#include <llvm/IR/Instruction.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Module.h>
#include "llvm/Support/InstIterator.h"
#include <llvm/Support/raw_ostream.h>
#include "DINO.h"

using namespace llvm;

bool DINOVersioner::doInitialization (Module &M) {
    verfn = M.getFunction(DINOVersionFunctionName);
    assert(verfn);

    return false;
}

/**
 * Outline:
 * Phase 1: In any order, mark all stores to NV variables.  Each function is
 *   associated with the set of NV variables it touches, and the set of
 *   StoreInsts that touch them.  These functions are provided by the
 *   DINOStoreAnalysis pass.
 * Phase 2: From each StoreInst, traverse the CFG backward along all paths.  On
 * each path:
 *   - If traversal encounters a task boundary, insert versioning (a fresh copy)
 *     of the var just before the task boundary (so that the version will be
 *     checkpointed when the task boundary fires), and stop.
 *   - If traversal encounters a function entry, add the var to the set of vars
 *     that the function touches, and stop.
 */
bool DINOVersioner::runOnModule (Module &M) {
    // Phase 1: tag stores to NV globals
    DINOStoreAnalysis &DSAn = getAnalysis<DINOStoreAnalysis>();

    // Phase 2
    for (auto &F : M.getFunctionList()) {
        if (F.getName().find(DINOPrefix) == 0) {
            outs() << "Skipping DINO function " << F.getName() << "\n";
            continue;
        }
        outs() << "Running DINOVersioner on function " << F.getName() << "\n";
        domTree = &getAnalysis<DominatorTree>(F);
        for (auto &NVS : *DSAn.getNVStores(F)) {
            Instruction *I = dyn_cast<Instruction>(NVS);

            for (auto &TB : DSAn.getTaskBoundaries(*I)) {
                outs() << "Found a task boundary: " << TB << "\n";

                CallInst::Create(verfn, "", TB);
            }
        }

    }

    return false;
}

bool DINOVersioner::doFinalization (Module &M) {
    return false;
}

char DINOVersioner::ID = 0;

DINOVersioner::DINOVersioner () : ModulePass(ID) {
}

DINOVersioner::~DINOVersioner () {
}

void DINOVersioner::getAnalysisUsage (AnalysisUsage &AU) const {
    AU.addRequired<DINOStoreAnalysis>();
    AU.addRequired<DominatorTree>();
}

const char *DINOVersioner::getPassName () const {
    return "DINO Versioning Transformation";
}

ModulePass *llvm::createDINOVersioner () {
    return new DINOVersioner();
}
